import { MCPClient } from './mcp-client';
import { MCPClientConfig, MCPTool, MCPResource, AgentMessage } from '../types/mcp';

export interface AgentConfig {
  name: string;
  description?: string;
  mcpClients: MCPClientConfig[];
  systemPrompt?: string;
  maxTokens?: number;
  temperature?: number;
}

export interface ToolCallResult {
  toolName: string;
  arguments: Record<string, any>;
  result: any;
  success: boolean;
  error?: string;
}

export class AgentMCPBridge {
  private config: AgentConfig;
  private mcpClients: Map<string, MCPClient> = new Map();
  private availableTools: Map<string, { client: MCPClient; tool: MCPTool }> = new Map();
  private availableResources: Map<string, { client: MCPClient; resource: MCPResource }> = new Map();

  constructor(config: AgentConfig) {
    this.config = {
      maxTokens: 4000,
      temperature: 0.7,
      ...config
    };
  }

  async initialize(): Promise<void> {
    console.log(`Initializing Agent ${this.config.name}...`);
    
    const connectionPromises = this.config.mcpClients.map(async (clientConfig) => {
      const client = new MCPClient(clientConfig);
      this.mcpClients.set(clientConfig.serverUrl, client);
      
      client.on('connected', (connection) => {
        console.log(`Connected to MCP server: ${connection.serverUrl}`);
      });
      
      client.on('error', (error) => {
        console.error(`MCP client error for ${clientConfig.serverUrl}:`, error);
      });
      
      await client.connect();
      
      await this.discoverCapabilities(client);
    });

    await Promise.all(connectionPromises);
    console.log(`Agent ${this.config.name} initialized successfully`);
  }

  private async discoverCapabilities(client: MCPClient): Promise<void> {
    try {
      const [tools, resources] = await Promise.all([
        client.listTools().catch(() => []),
        client.listResources().catch(() => [])
      ]);

      tools.forEach((tool: MCPTool) => {
        const key = `${tool.name}`;
        this.availableTools.set(key, { client, tool });
        console.log(`Discovered tool: ${tool.name}`);
      });

      resources.forEach((resource: MCPResource) => {
        const key = `${resource.uri}`;
        this.availableResources.set(key, { client, resource });
        console.log(`Discovered resource: ${resource.uri}`);
      });
    } catch (error) {
      console.error('Failed to discover capabilities:', error);
    }
  }

  getAvailableTools(): MCPTool[] {
    return Array.from(this.availableTools.values()).map(item => item.tool);
  }

  getAvailableResources(): MCPResource[] {
    return Array.from(this.availableResources.values()).map(item => item.resource);
  }

  async processMessage(message: AgentMessage): Promise<AgentMessage> {
    console.log(`Processing ${message.role} message...`);
    
    if (message.role === 'user') {
      return await this.handleUserMessage(message);
    }
    
    return message;
  }

  private async handleUserMessage(message: AgentMessage): Promise<AgentMessage> {
    const tools = this.getAvailableTools();
    
    if (tools.length === 0) {
      return {
        role: 'assistant',
        content: 'I don\'t have access to any tools right now. Please check the MCP server connections.'
      };
    }

    const response = await this.generateResponse(message.content, tools);
    
    if (response.tool_calls) {
      const toolResults = await this.executeToolCalls(response.tool_calls);
      return await this.generateResponseWithToolResults(
        message.content, 
        response.content, 
        toolResults
      );
    }

    return {
      role: 'assistant',
      content: response.content
    };
  }

  private async generateResponse(userMessage: string, tools: MCPTool[]): Promise<any> {
    const toolSchemas = tools.map(tool => ({
      type: 'function',
      function: {
        name: tool.name,
        description: tool.description,
        parameters: tool.inputSchema
      }
    }));

    const messages = [
      {
        role: 'system' as const,
        content: this.config.systemPrompt || 'You are a helpful assistant with access to various tools.'
      },
      {
        role: 'user' as const,
        content: userMessage
      }
    ];

    try {
      const response = await this.callLLM(messages, toolSchemas);
      return response;
    } catch (error) {
      console.error('Failed to generate response:', error);
      return {
        content: 'I apologize, but I encountered an error while processing your request.'
      };
    }
  }

  private async executeToolCalls(toolCalls: any[]): Promise<ToolCallResult[]> {
    const results: ToolCallResult[] = [];
    
    for (const toolCall of toolCalls) {
      const { name, arguments: args } = toolCall.function;
      
      try {
        const toolInfo = this.availableTools.get(name);
        if (!toolInfo) {
          throw new Error(`Tool ${name} not found`);
        }

        const result = await toolInfo.client.callTool(name, args);
        
        results.push({
          toolName: name,
          arguments: args,
          result,
          success: true
        });
        
        console.log(`Tool ${name} executed successfully`);
        
      } catch (error) {
        console.error(`Tool ${name} execution failed:`, error);
        results.push({
          toolName: name,
          arguments: args,
          result: null,
          success: false,
          error: error instanceof Error ? error.message : 'Unknown error'
        });
      }
    }
    
    return results;
  }

  private async generateResponseWithToolResults(
    originalMessage: string, 
    initialResponse: string, 
    toolResults: ToolCallResult[]
  ): Promise<AgentMessage> {
    // 如果有工具调用结果，进入多轮处理模式
    return await this.handleMultiRoundToolCalls(originalMessage, [
      {
        role: 'assistant',
        content: initialResponse,
        toolResults: toolResults
      }
    ]);
  }

  private async handleMultiRoundToolCalls(
    originalMessage: string,
    conversationHistory: Array<{
      role: 'assistant' | 'system';
      content: string;
      toolResults?: ToolCallResult[];
      toolCalls?: any[];
    }>
  ): Promise<AgentMessage> {
    const maxRounds = 5; // 防止无限循环，最多5轮工具调用
    let currentRound = 0;
    
    while (currentRound < maxRounds) {
      currentRound++;
      
      // 构建对话历史
      const messages = [
        {
          role: 'system' as const,
          content: this.config.systemPrompt || 'You are a helpful assistant with access to various tools. You can call multiple tools in sequence to complete complex tasks.'
        },
        {
          role: 'user' as const,
          content: originalMessage
        }
      ];

      // 添加对话历史
      for (const turn of conversationHistory) {
        messages.push({
          role: turn.role as 'system', // 强制转换为system以符合类型要求
          content: turn.content
        });

        // 如果有工具调用结果，添加为系统消息
        if (turn.toolResults && turn.toolResults.length > 0) {
          const toolResultsText = turn.toolResults.map(result => {
            if (result.success) {
              return `Tool ${result.toolName} returned: ${JSON.stringify(result.result, null, 2)}`;
            } else {
              return `Tool ${result.toolName} failed: ${result.error}`;
            }
          }).join('\n\n');
          
          messages.push({
            role: 'system' as const,
            content: `Tool execution results (Round ${currentRound - 1}):\n${toolResultsText}`
          });
        }
      }

      try {
        // 调用LLM获取响应
        const response = await this.callLLM(messages, this.getAvailableTools());
        
        // 检查是否有新的工具调用
        if (response.tool_calls && response.tool_calls.length > 0) {
          console.log(`Round ${currentRound}: LLM requested ${response.tool_calls.length} more tool calls`);
          
          // 执行新的工具调用
          const newToolResults = await this.executeToolCalls(response.tool_calls);
          
          // 添加到对话历史
          conversationHistory.push({
            role: 'assistant',
            content: response.content,
            toolCalls: response.tool_calls,
            toolResults: newToolResults
          });
          
          // 检查是否所有工具调用都成功
          const allSuccess = newToolResults.every(result => result.success);
          if (!allSuccess) {
            console.log('Some tool calls failed, stopping tool execution');
            break;
          }
          
          // 继续下一轮
          continue;
        } else {
          // 没有新的工具调用，返回最终响应
          console.log(`Round ${currentRound}: No more tool calls, returning final response`);
          return {
            role: 'assistant',
            content: response.content
          };
        }
        
      } catch (error) {
        console.error(`Error in round ${currentRound}:`, error);
        
        // 返回到目前为止的结果，加上错误信息
        const lastResponse = conversationHistory[conversationHistory.length - 1];
        return {
          role: 'assistant',
          content: `${lastResponse.content}\n\nI encountered an error while processing your request: ${error instanceof Error ? error.message : 'Unknown error'}`
        };
      }
    }
    
    // 达到最大轮数限制，返回最后的结果
    console.log(`Reached maximum rounds (${maxRounds}), returning final response`);
    const lastResponse = conversationHistory[conversationHistory.length - 1];
    return {
      role: 'assistant',
      content: `${lastResponse.content}\n\nI've completed several tool calls but may need more information to fully answer your question.`
    };
  }

  private async callLLM(messages: any[], tools: any[]): Promise<any> {
    console.log('Calling LLM with messages:', messages.length, 'and tools:', tools.length);
    
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    const userMessage = messages.find(m => m.role === 'user')?.content || '';
    const lastAssistantMessage = messages.filter(m => m.role === 'assistant').pop()?.content || '';
    const systemMessages = messages.filter(m => m.role === 'system');
    
    // 检查是否有工具执行结果
    const hasToolResults = systemMessages.some(m => m.content.includes('Tool execution results'));
    
    // 多轮工具调用逻辑示例
    if (hasToolResults) {
      console.log('Processing tool results and deciding next steps...');
      
      // 模拟LLM分析工具结果并决定是否需要更多工具
      if (userMessage.includes('分析') && userMessage.includes('天气') && !lastAssistantMessage.includes('温度转换')) {
        // 第一轮：获得了天气，现在需要温度转换
        return {
          content: '我已经获得了北京的天气信息。现在让我帮您进行温度转换，并提供更详细的分析。',
          tool_calls: [
            {
              id: 'call_2',
              type: 'function',
              function: {
                name: 'calculate',
                arguments: JSON.stringify({ expression: '25 * 9/5 + 32' }) // 摄氏转华氏
              }
            }
          ]
        };
      } else if (userMessage.includes('比较') && userMessage.includes('上海') && !lastAssistantMessage.includes('上海天气')) {
        // 需要获取上海的天气进行比较
        return {
          content: '让我获取上海的天气信息来进行比较。',
          tool_calls: [
            {
              id: 'call_2',
              type: 'function',
              function: {
                name: 'get_weather',
                arguments: JSON.stringify({ location: 'Shanghai' })
              }
            }
          ]
        };
      } else if (userMessage.includes('统计') && !lastAssistantMessage.includes('统计分析')) {
        // 需要更多数据来进行统计分析
        return {
          content: '让我获取更多城市的数据来进行统计分析。',
          tool_calls: [
            {
              id: 'call_2',
              type: 'function',
              function: {
                name: 'get_weather',
                arguments: JSON.stringify({ location: 'Shanghai' })
              }
            },
            {
              id: 'call_3',
              type: 'function',
              function: {
                name: 'get_weather',
                arguments: JSON.stringify({ location: 'Guangzhou' })
              }
            }
          ]
        };
      } else {
        // 已经有足够的信息，生成最终响应
        return {
          content: this.generateFinalResponse(userMessage, systemMessages)
        };
      }
    }
    
    // 初始请求处理
    if (userMessage.toLowerCase().includes('weather') && tools.some(t => t.function?.name?.includes('weather'))) {
      return {
        content: '我来帮您查询天气信息。让我先获取北京的天气数据。',
        tool_calls: [
          {
            id: 'call_1',
            type: 'function',
            function: {
              name: 'get_weather',
              arguments: JSON.stringify({ location: 'Beijing' })
            }
          }
        ]
      };
    }
    
    if (userMessage.toLowerCase().includes('time') || userMessage.toLowerCase().includes('date')) {
      return {
        content: `The current time is ${new Date().toLocaleString('zh-CN')}.`
      };
    }
    
    return {
      content: `I understand you said: "${userMessage}". I'm a demo agent and can help you with various tasks using the available tools.`
    };
  }

  private generateFinalResponse(userMessage: string, systemMessages: any[]): string {
    // 根据系统消息中的工具结果生成最终响应
    const toolResultsText = systemMessages
      .filter(m => m.content.includes('Tool execution results'))
      .map(m => m.content)
      .join('\n');
    
    if (userMessage.includes('分析') && userMessage.includes('天气')) {
      return `基于天气数据的分析完成：

📊 **天气分析报告**：
${toolResultsText}

🌡️ **温度分析**：
- 北京当前气温适中，建议日常穿着
- 湿度水平良好，体感舒适
- 天气状况良好，适合外出活动

💡 **建议**：
- 适合进行户外活动
- 建议携带轻便外套
- 注意防晒和补水

这是一个多轮工具调用的示例，展示了如何先获取数据，然后进行分析，最后提供建议。`;
    }
    
    if (userMessage.includes('比较')) {
      return `城市天气比较完成：

📈 **比较结果**：
${toolResultsText}

🏙️ **差异分析**：
- 两城市温度差异：需要根据实际数据计算
- 湿度对比：影响体感舒适度
- 天气状况对比：影响出行安排

📋 **总结**：
通过多轮数据获取和分析，完成了城市间天气的全面比较。`;
    }
    
    if (userMessage.includes('统计')) {
      return `多城市天气统计分析完成：

📊 **统计数据**：
${toolResultsText}

🔢 **分析结果**：
- 覆盖多个城市的天气数据
- 温度分布情况
- 湿度和天气状况统计

📈 **结论**：
通过多轮数据收集，完成了区域天气的统计分析。`;
    }
    
    return `多轮工具调用完成。基于收集的数据和分析，完成了您的请求：${userMessage}`;
  }

  async getResource(resourceUri: string): Promise<any> {
    const resourceInfo = this.availableResources.get(resourceUri);
    if (!resourceInfo) {
      throw new Error(`Resource ${resourceUri} not found`);
    }
    
    return await resourceInfo.client.getResource(resourceUri);
  }

  getStatus(): {
    name: string;
    connectedClients: number;
    availableTools: number;
    availableResources: number;
    clientStatuses: Array<{ url: string; connected: boolean }>;
  } {
    const clientStatuses = Array.from(this.mcpClients.entries()).map(([url, client]) => ({
      url,
      connected: client.isConnected()
    }));

    return {
      name: this.config.name,
      connectedClients: clientStatuses.filter(s => s.connected).length,
      availableTools: this.availableTools.size,
      availableResources: this.availableResources.size,
      clientStatuses
    };
  }

  async shutdown(): Promise<void> {
    console.log(`Shutting down Agent ${this.config.name}...`);
    
    const disconnectPromises = Array.from(this.mcpClients.values()).map(client => 
      client.disconnect().catch(error => {
        console.error('Error disconnecting MCP client:', error);
      })
    );

    await Promise.all(disconnectPromises);
    
    this.availableTools.clear();
    this.availableResources.clear();
    this.mcpClients.clear();
    
    console.log(`Agent ${this.config.name} shutdown complete`);
  }
}